<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/favicon.ico">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon.ico">
  <link rel="mask-icon" href="/images/favicon.ico" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"daorongxing.gitee.io","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":true,"scrollpercent":true},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":true,"mediumzoom":false,"lazyload":false,"pangu":true,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="1. 如何判断对象可以被回收1.1 引用计数法通过调用对象的次数计数，调用了一次+1，调用了两次+2；弊端：循环引用可能导致计数次数无法归零，进而导致内存泄漏。 1.2 可达性分析算法先确立一个根对象(GC Root)，看有没有对象和根对象直接或者间接的引用，如果有，那就是不能被垃圾回收的对象。可作为GC Root对象的有：  方法堆栈中的参数，局部变量，临时变量等 引用类型的静态变量 字符串常量">
<meta property="og:type" content="article">
<meta property="og:title" content="GC">
<meta property="og:url" content="https://daorongxing.gitee.io/post/370/index.html">
<meta property="og:site_name" content="邢道荣的个人博客">
<meta property="og:description" content="1. 如何判断对象可以被回收1.1 引用计数法通过调用对象的次数计数，调用了一次+1，调用了两次+2；弊端：循环引用可能导致计数次数无法归零，进而导致内存泄漏。 1.2 可达性分析算法先确立一个根对象(GC Root)，看有没有对象和根对象直接或者间接的引用，如果有，那就是不能被垃圾回收的对象。可作为GC Root对象的有：  方法堆栈中的参数，局部变量，临时变量等 引用类型的静态变量 字符串常量">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://daorongxing.gitee.io/images/Mark-Sweep.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/Mark-Copy.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/Appel.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/GC.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/VMparams.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/serial.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/CMS.png">
<meta property="og:image" content="https://daorongxing.gitee.io/images/G1.png">
<meta property="article:published_time" content="2022-11-25T10:50:12.000Z">
<meta property="article:modified_time" content="2022-11-30T08:55:22.794Z">
<meta property="article:author" content="Daorong Xing">
<meta property="article:tag" content="JVM">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://daorongxing.gitee.io/images/Mark-Sweep.png">

<link rel="canonical" href="https://daorongxing.gitee.io/post/370/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>GC | 邢道荣的个人博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">邢道荣的个人博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://daorongxing.gitee.io/post/370/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Daorong Xing">
      <meta itemprop="description" content="这是我的个人博客，记录计算机学习之路！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="邢道荣的个人博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          GC
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-11-25 18:50:12" itemprop="dateCreated datePublished" datetime="2022-11-25T18:50:12+08:00">2022-11-25</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-11-30 16:55:22" itemprop="dateModified" datetime="2022-11-30T16:55:22+08:00">2022-11-30</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
                </span>
            </span>

          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>9.6k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>9 分钟</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="1-如何判断对象可以被回收"><a href="#1-如何判断对象可以被回收" class="headerlink" title="1. 如何判断对象可以被回收"></a>1. 如何判断对象可以被回收</h1><h2 id="1-1-引用计数法"><a href="#1-1-引用计数法" class="headerlink" title="1.1 引用计数法"></a>1.1 引用计数法</h2><p>通过调用对象的次数计数，调用了一次+1，调用了两次+2；<br>弊端：循环引用可能导致计数次数无法归零，进而导致内存泄漏。</p>
<h2 id="1-2-可达性分析算法"><a href="#1-2-可达性分析算法" class="headerlink" title="1.2 可达性分析算法"></a>1.2 可达性分析算法</h2><p>先确立一个根对象(<code>GC Root</code>)，看有没有对象和根对象直接或者间接的引用，如果有，那就是不能被垃圾回收的对象。<br>可作为<code>GC Root</code>对象的有：</p>
<ul>
<li>方法堆栈中的参数，局部变量，临时变量等</li>
<li>引用类型的静态变量</li>
<li>字符串常量池中的引用</li>
<li>本地方法栈中引用的对象</li>
<li>Class对象，异常对象，系统类加载器，本地代码缓存等</li>
</ul>
<h2 id="1-3-四种引用"><a href="#1-3-四种引用" class="headerlink" title="1.3 四种引用"></a>1.3 四种引用</h2><h3 id="1-3-1-强引用"><a href="#1-3-1-强引用" class="headerlink" title="1.3.1 强引用"></a>1.3.1 强引用</h3><p>沿着根对象能找到的A对象，那么A对象称为被强引用。仅当强引用与A对象断开时，可被回收。<br></p>
<h3 id="1-3-2-软引用"><a href="#1-3-2-软引用" class="headerlink" title="1.3.2 软引用"></a>1.3.2 软引用</h3><p><code>SoftRenference&lt;byte[]&gt; ref = new SoftRenference&lt;&gt;(new byte[])</code><br><strong>没有被强引用所引用</strong>，垃圾回收发生时都<u>有可能</u>被回收。<br>当发生过一次垃圾回收且内存仍然不够时，会回收<strong>被软引用引用的对象</strong>。<br>软引用一般适用于一些非必要的场景，比如说网页图片，没有必要一直占着内存空间，等到需要的时候再加载就可以了。</p>
<ul>
<li>具体案例如下：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">soft</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// list --&gt; SoftReference --&gt; byte[]</span></span><br><span class="line"></span><br><span class="line">    List&lt;SoftReference&lt;<span class="type">byte</span>[]&gt;&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">        SoftReference&lt;<span class="type">byte</span>[]&gt; ref = <span class="keyword">new</span> <span class="title class_">SoftReference</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">byte</span>[_4MB]);</span><br><span class="line">        System.out.println(ref.get());</span><br><span class="line">        list.add(ref);</span><br><span class="line">        System.out.println(list.size());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(<span class="string">&quot;循环结束：&quot;</span> + list.size());</span><br><span class="line">    <span class="keyword">for</span> (SoftReference&lt;<span class="type">byte</span>[]&gt; ref : list) &#123;</span><br><span class="line">        System.out.println(ref.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<strong>输出结果：</strong><blockquote>
<p>[B@330bedb4<br>1<br>[B@2503dbd3<br>2<br>[B@4b67cf4d<br>3<br>[GC (Allocation Failure) [PSYoungGen: 2162K-&gt;488K(6144K)] 14450K-&gt;13074K(19968K), 0.0019161 secs] [Times: user&#x3D;0.00 sys&#x3D;0.00, real&#x3D;0.00 secs]<br><mark>内存不足，进行一次垃圾回收。</mark><br>[B@7ea987ac<br>4<br>[GC (Allocation Failure) –[PSYoungGen: 4696K-&gt;4696K(6144K)] 17282K-&gt;17290K(19968K), 0.0006865 secs] [Times: user&#x3D;0.00 sys&#x3D;0.00, real&#x3D;0.00 secs]<br>[Full GC (Ergonomics) [PSYoungGen: 4696K-&gt;4535K(6144K)] [ParOldGen: 12594K-&gt;12545K(13824K)] 17290K-&gt;17080K(19968K), [Metaspace: 3373K-&gt;3373K(1056768K)], 0.&gt; &gt; 0045075 secs] [Times: user&#x3D;0.00 sys&#x3D;0.00, real&#x3D;0.01 secs]<br>[GC (Allocation Failure) –[PSYoungGen: 4535K-&gt;4535K(6144K)] 17080K-&gt;17096K(19968K), 0.0006433 secs] [Times: user&#x3D;0.00 sys&#x3D;0.00, real&#x3D;0.00 secs]<br>[Full GC (Allocation Failure) [PSYoungGen: 4535K-&gt;0K(6144K)] [ParOldGen: 12561K-&gt;677K(8704K)] 17096K-&gt;677K(14848K), [Metaspace: 3373K-&gt;3373K(1056768K)], 0.&gt; &gt; 0059614 secs] [Times: user&#x3D;0.01 sys&#x3D;0.00, real&#x3D;0.01 secs]<br><mark>进行一次垃圾回收之后内存仍然不足，触发软引用机制，将软引用创建的对象所垃圾回收释放空间。</mark><br>[B@12a3a380<br>5<br>循环结束：5<br>null<br>null<br>null<br>null<br>[B@12a3a380</p>
</blockquote>
</li>
</ul>
<h3 id="1-3-3-引用队列"><a href="#1-3-3-引用队列" class="headerlink" title="1.3.3 引用队列"></a>1.3.3 引用队列</h3><p>软弱引用无引用对象时，自动进入引用队列</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">_4MB</span> <span class="operator">=</span> <span class="number">4</span> * <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    List&lt;SoftReference&lt;<span class="type">byte</span>[]&gt;&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 引用队列</span></span><br><span class="line">    ReferenceQueue&lt;<span class="type">byte</span>[]&gt; queue = <span class="keyword">new</span> <span class="title class_">ReferenceQueue</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">        <span class="comment">// 关联了引用队列， 当软引用所关联的 byte[]被回收时，软引用自己会加入到 queue 中去</span></span><br><span class="line">        SoftReference&lt;<span class="type">byte</span>[]&gt; ref = <span class="keyword">new</span> <span class="title class_">SoftReference</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">byte</span>[_4MB], queue);</span><br><span class="line">        System.out.println(ref.get());</span><br><span class="line">        list.add(ref);</span><br><span class="line">        System.out.println(list.size());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中获取无用的 软引用对象，并移除</span></span><br><span class="line">    Reference&lt;? <span class="keyword">extends</span> <span class="title class_">byte</span>[]&gt; poll = queue.poll();</span><br><span class="line">    <span class="keyword">while</span>( poll != <span class="literal">null</span>) &#123;</span><br><span class="line">        list.remove(poll);</span><br><span class="line">        poll = queue.poll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">&quot;===========================&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (SoftReference&lt;<span class="type">byte</span>[]&gt; reference : list) &#123;</span><br><span class="line">        System.out.println(reference.get());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>输出：</p>
<blockquote>
<p>[B@4aa8f0b4 (最后结果)</p>
</blockquote>
<h3 id="1-3-4-弱引用"><a href="#1-3-4-弱引用" class="headerlink" title="1.3.4 弱引用"></a>1.3.4 弱引用</h3><p>只要当发生了垃圾回收，被<strong>弱引用引用的对象</strong>会被回收。<br>类似于软引用。</p>
<h3 id="1-3-5-虚引用"><a href="#1-3-5-虚引用" class="headerlink" title="1.3.5 虚引用"></a>1.3.5 虚引用</h3><p>必须配合引用队列来使用。创建时即关联引用队列。缓存时会留下直接内存，当<code>StringBuffer</code>对象被回收时，他的直接内存没有被回收掉，此时虚引用对象进入引用队列，调用线程清理直接内存。</p>
<h3 id="1-3-6-终结器引用"><a href="#1-3-6-终结器引用" class="headerlink" title="1.3.6 终结器引用"></a>1.3.6 终结器引用</h3><p>当<code>finallize()</code>将要被垃圾回收时，将终结器引用放入引用队列，由<code>finallizeHandler</code>去由终结器引用的对象去销毁对象<br>缺点：<code>finallizeHandler</code>优先级很低，且销毁流程漫长，需要两次GC才能销毁完成，易造成内存泄漏 。</p>
<h1 id="2-三种垃圾收集算法"><a href="#2-三种垃圾收集算法" class="headerlink" title="2. 三种垃圾收集算法"></a>2. 三种垃圾收集算法</h1><h2 id="2-1-标记——清除算法"><a href="#2-1-标记——清除算法" class="headerlink" title="2.1 标记——清除算法"></a>2.1 标记——清除算法</h2><ul>
<li>流程：首先标记出需要垃圾回收的对象，标记完成后，统一回收被标记的对象。也可以相反。</li>
<li>缺点：<br>执行效率不稳定，随着对象增长降低效率不断变化；<br>内存空间碎片化，需要分配较大内存时无法找到足够连续内存不得不提前进行一次垃圾回收操作。<br><img src="/images/Mark-Sweep.png"></li>
</ul>
<h2 id="2-2-标记——复制算法"><a href="#2-2-标记——复制算法" class="headerlink" title="2.2 标记——复制算法"></a>2.2 标记——复制算法</h2><ul>
<li>流程：<strong>半区复制。</strong> 将可用内存按容量划分为大小相等的两块，每次只使用其中的一块，这一块的内存用完了，就将存活着的对象复制到另一块的上面，然后再把已使用的内存空间一次性清理掉。</li>
<li>优点：解决了碎片空间的问题，只需要移动栈顶指针，按顺序分配即可</li>
<li>缺点：内存缩小到原来的一半，造成空间浪费<br><img src="/images/Mark-Copy.png"></li>
</ul>
<p>拓展：APPel式回收<br>把新生代分为一块较大的Eden空间和两块较小的Survivor空间，每次分配内存只使用一块Eden和一块Survivor。发生垃圾收集时，将Eden和Survivor仍然存活的对象一次性复制到另一块Survivor中，直接清理掉原来的两块对象。<br><img src="/images/Appel.png"></p>
<blockquote>
<p>理论依据：HotSpot默认Eden和Survivor的大小比为8：1，即新生代内存空间占总内存空间的90%，相比于标记复制算法节约了巨大的内存空间，而“朝生夕灭”理论认为，新生代中有98%的对象熬不过第一轮收集，所以内存空间时绰绰有余的。</p>
</blockquote>
<h2 id="2-3-标记——整理算法"><a href="#2-3-标记——整理算法" class="headerlink" title="2.3 标记——整理算法"></a>2.3 标记——整理算法</h2><ul>
<li>流程：标记算法和前两者一样，而在回收的时候不同于前者，是先让所有存活的对象往内存空间的一侧进行移动，然后直接清理掉边界之外的内存。</li>
<li>缺点：移动更新对象任务量及其庞大，需要全程暂停用户应用程序才能进行。易形成<code>Stop The World</code>现象。</li>
<li>移动和不移动的利弊比较：<br>是否移动对象都存在弊端，<strong>移动则内存回收</strong>时会更复杂，<strong>不移动则内存分配</strong>时会更复杂；从<strong>垃圾收集的停顿时间</strong>来看，<strong>不移动</strong>对象停顿时间会更短，甚至可以不需要停顿，但是从整个<strong>程序的吞吐量</strong>来看，<strong>移动对象会更划算</strong>；即使不移动对象会使得收集器的效率提升一些，但因内存分配和访问相比垃圾收集频率要 高得多，这部分的耗时增加，总吞吐量仍然是下降的</li>
</ul>
<h2 id="3-垃圾分代回收"><a href="#3-垃圾分代回收" class="headerlink" title="3. 垃圾分代回收"></a>3. 垃圾分代回收</h2><h2 id="3-1-分代垃圾回收机制"><a href="#3-1-分代垃圾回收机制" class="headerlink" title="3.1 分代垃圾回收机制"></a>3.1 分代垃圾回收机制</h2><p><img src="/images/GC.png"></p>
<ul>
<li>对象首先分配在伊甸园区域</li>
<li>新生代空间不足时，触发 minor gc，伊甸园和 from 存活的对象使用 copy 复制到 to 中，存活的对象年龄加 1并且交换 from to。</li>
<li>minor gc 会引发 stop the world，暂停其它用户的线程，等垃圾回收结束，用户线程才恢复运行</li>
<li>当对象寿命超过阈值时，会晋升至老年代，最大寿命是15（4bit）。</li>
<li>当老年代空间不足，会先尝试触发 minor gc，如果之后空间仍不足，那么触发 full gc，STW的时间更长。</li>
<li>当新生代内存无法容纳对象大小，直接晋升为老年代</li>
<li>线程中出现OOM异常时，他占据的内存资源会全部释放掉，不会影响其他线程的运行</li>
</ul>
<h2 id="3-2-VM相关参数"><a href="#3-2-VM相关参数" class="headerlink" title="3.2 VM相关参数"></a>3.2 VM相关参数</h2><p><img src="/images/VMparams.png"></p>
<h1 id="4-垃圾回收器"><a href="#4-垃圾回收器" class="headerlink" title="4. 垃圾回收器"></a>4. 垃圾回收器</h1><blockquote>
<p>serial: 串行    单线程,一个方法执行完下个方法才能够执行<br>parallel : 并行   多线程,可以同时执行垃圾回收, 但其他无法方法运行 STW 相当于所有线程都归垃圾回收所有<br>concurrent : 并发   多线程,其他方法和垃圾回收可以同时执行,但是只能逐个进行垃圾回收,相当于平等的线程分配给每一个方法</p>
</blockquote>
<h2 id="4-1-串行垃圾回收器-Serial-x2F-Serial-Old"><a href="#4-1-串行垃圾回收器-Serial-x2F-Serial-Old" class="headerlink" title="4.1 串行垃圾回收器(Serial&#x2F; Serial Old)"></a>4.1 串行垃圾回收器(Serial&#x2F; Serial Old)</h2><p>单线程。适用于堆内存较少的情况，适合个人电脑<br><code>-XX:+UseSerialGC = Serial + SerialOld</code><br><img src="/images/serial.png"></p>
<h2 id="4-2-吞吐量优先-Parallel-x2F-Parallel-Old"><a href="#4-2-吞吐量优先-Parallel-x2F-Parallel-Old" class="headerlink" title="4.2 吞吐量优先(Parallel&#x2F; Parallel Old)"></a>4.2 吞吐量优先(Parallel&#x2F; Parallel Old)</h2><p>多线程，堆内存较大，多核CPU<br>单位时间内,完成STW次数越少(STW的时间占总用户时间)<br><code>XX:+UseparalellGC</code><br><code>-XX:ParallelGCThreads:n</code> :允许同时允许的线程数量<br><code>-XX:+UseAdaptSizePolicy</code> :自适应调整新生代的大小<br><code>-XX:GCTimeRadio:radio</code> : 最多允许<strong>垃圾回收时间占总线程时间</strong>的<mark>1&#x2F;(1+radio)</mark>,希望堆大<br><code>-XX:MaxGCPauseMillis=is</code> : default是200ms,表示<strong>最长垃圾回收时间</strong>,希望堆小,与上一个冲突</p>
<h2 id="4-2-响应时间优先-CMS-Concurrent-Marking-Sweep"><a href="#4-2-响应时间优先-CMS-Concurrent-Marking-Sweep" class="headerlink" title="4.2 响应时间优先(CMS Concurrent Marking Sweep)"></a>4.2 响应时间优先(CMS Concurrent Marking Sweep)</h2><p>多线程，堆内存较大，多核CPU<br>注重STW时间的长度(单独只考虑STW的时间)</p>
<ul>
<li>初始标记: 需要STW，但仅标记与GC Root关联的对象,速度快</li>
<li>并发标记: 从GC Root直接关联对象开始遍历整个对象图, 耗时长,但并发运行</li>
<li>重新标记: 需要STW, 修正因并发导致的对象标记变动</li>
<li>并发清除: 清理标记对象<br><strong><font color=red>只有在初始标记和重新标记时候才会STW</font></strong><br>三个缺点:</li>
<li>对CPU占用低,只占用了一部分进行GC, 但拖慢了用户进行的CPU量, 对吞吐量有影响.</li>
<li>并发过程中产生的浮动垃圾难以处理</li>
<li>大量空间碎片的产生<br><code>-XX:+UseConcMarkSweepGC</code><br><code>-XX:ConcGCThreads=thread</code>:运用于垃圾回收的线程,一般是总线程数的1&#x2F;4<br><code>-XX:CMSInitiatingOccupancyFraction : percent</code> :触发垃圾回收阈值<br>剩下内存空间留给其他进行的进程以及浮动垃圾; 阈值过高可能导致并发失败<br><img src="/images/CMS.png"></li>
</ul>
<h1 id="5-G1"><a href="#5-G1" class="headerlink" title="5. G1"></a>5. G1</h1><blockquote>
<p>难点，一款很多知识点的垃圾回收器，牵涉到很多新概念</p>
</blockquote>
<h2 id="5-1-基础概念的了解"><a href="#5-1-基础概念的了解" class="headerlink" title="5.1 基础概念的了解"></a>5.1 基础概念的了解</h2><h3 id="5-1-1-写屏障"><a href="#5-1-1-写屏障" class="headerlink" title="5.1.1 写屏障"></a>5.1.1 写屏障</h3><p>可以看成是虚拟机层面对”引用类型字段赋值”这个动作的AOP切面, 在引用对象赋值的时候会形成一个环形,供程序执行额外的操作,而我们解决这种想要在编译场景中赋值的操作,就可以使用写屏障这种在机械码方面操作的手段,简单的来说,写屏障就是负责对在编译阶段中产生改变内容的处理的.</p>
<h3 id="5-1-2-记忆集和卡表"><a href="#5-1-2-记忆集和卡表" class="headerlink" title="5.1.2 记忆集和卡表"></a>5.1.2 记忆集和卡表</h3><p>这两者的出现是为了解决对象跨代引用所带来的问题的.跨代引用会导致老年代向新生代的引用难以通过只扫描新生代的对象去识别, 而如果要进行对老年代和新生代进行同时扫描的话, 那么STW的时间会变长, 性能较差.而记忆集是一种抽象的数据结构, 主要是用于记录非搜集区域指向搜集区域的指针集合的抽象数据结构.其在G1垃圾处理器中被安排在新生代中.而卡表是记忆集的一种具体实现.卡表主要运用在老年代, 当有一个老年代对象引用了新生代的时候, 卡表就会在对应的数组元素值标记为1, 表示这个元素为脏.之后通过写屏障在引用对象的行为发生时进行标记, 之后在新生代垃圾回收扫描时,这个对象就会被视为活对象了.</p>
<h2 id="5-2-三色标记算法"><a href="#5-2-三色标记算法" class="headerlink" title="5.2 三色标记算法"></a>5.2 三色标记算法</h2><h3 id="5-2-1-基本概念"><a href="#5-2-1-基本概念" class="headerlink" title="5.2.1 基本概念"></a>5.2.1 基本概念</h3><p>这是垃圾回收在新生代回收时根据GC Root的遍历所进行标记的一种算法.具体实现为将对象标为黑灰白三种对象, 其划分标准如下: </p>
<ul>
<li>黑: 已经被垃圾回收器访问过, 其所有引用已经全部被扫描过了, 保证其是安全存活的, 且不会再进行扫描;</li>
<li>灰: 被垃圾回收器访问过, 但是他身上的至少一个引用还没有被垃圾回收器访问, 是扫描引用他的灰色对象之后形成的;</li>
<li>白: 没有被垃圾回收器访问过, 开始的时候所有对象都是白色的, 当垃圾回收扫描完成时, 还是白色的对象说明对象是不可达的.<br>扫描完成的标志: 没有灰色对象的存在</li>
</ul>
<h3 id="5-2-2-遍历过程"><a href="#5-2-2-遍历过程" class="headerlink" title="5.2.2 遍历过程"></a>5.2.2 遍历过程</h3><p>1.初始时，全部对象都是白色的<br>2.GC Roots直接引用的对象变成灰色<br>3.从灰色集合中获取元素：<br>    3.1 将本对象直接引用的对象标记为灰色<br>    3.2 将本对象标记为黑色<br>4.重复步骤3，直到灰色的对象集合变为空<br>5.结束后，仍然被标记为白色的对象就是不可达对象，视为垃圾对象</p>
<h3 id="5-2-3-存在问题"><a href="#5-2-3-存在问题" class="headerlink" title="5.2.3 存在问题"></a>5.2.3 存在问题</h3><p>标记算法存在的问题都是基于并发执行下产生的, 因为用户线程在进行的时候, 对象的调用总是会进行, 而同时进行对对象的操作有可能导致引用的错误.</p>
<ol>
<li>漏删<br>把原本死亡的对象标记为存活, 会发生的情况为因为A到B的引用,B被标灰了,但之后引用就断开了, 此时B没有对象引用他, 但是他的标记是灰色, 不会被回收, 这种我们把它叫做浮动垃圾. 这种解决方案十分简单, 下次扫描的时候, 因为没有对象引用他, 他就会自动被删除的.</li>
<li>多删<br>原本是黑色的对象被标记为白色.具体造成原因为如下两点:</li>
</ol>
<ul>
<li>插入了一条或者多条黑色对象对白色对象的引用</li>
<li>删除了所有灰色对象对白色对象的直接或者间接引用<br>这种问题很大, 因为本来程序运行所需要的一个对象被销毁了, 会导致程序的异常, 那么垃圾回收器给出了我们两种如下解决方案.</li>
</ul>
<h3 id="5-2-4-解决方案"><a href="#5-2-4-解决方案" class="headerlink" title="5.2.4 解决方案"></a>5.2.4 解决方案</h3><ul>
<li>增量更新(Increment update)<br>破坏的是第一个条件, 他会将这个新插入的引用记录下来, 扫描结束之后会以黑色对象为根重新进行扫描,可以理解为这个技术可以使得产生的新引用的黑色节点变灰.其主要实现为写屏障; 主要用于CMS中</li>
<li>原始快照(SnapShot At The Beginning)<br>破坏的主要是第二个条件, 他会在断开连接时将断开连接前的一瞬间记录下来, 再将他放到一个独立的内存区域中, 扫描结束后, 重新以灰色节点为根重新进行扫描.</li>
</ul>
<h2 id="5-3-G1垃圾回收器的垃圾处理流程"><a href="#5-3-G1垃圾回收器的垃圾处理流程" class="headerlink" title="5.3 G1垃圾回收器的垃圾处理流程"></a>5.3 G1垃圾回收器的垃圾处理流程</h2><h3 id="5-3-1-以rigion为分区的回收范围改变"><a href="#5-3-1-以rigion为分区的回收范围改变" class="headerlink" title="5.3.1 以rigion为分区的回收范围改变"></a>5.3.1 以rigion为分区的回收范围改变</h3><p>首先我们需要知道，G1相较于CMS包括之前的垃圾回收器做出了一个巨大的改变，就是垃圾回收区域的划分，以前分代收集理论是垃圾回收器的主流理论，而G1将其做了一个更新包装，其面向堆内任何部分来组成回收集(Cset)，每一个回收集被叫做region区域，每一个区域中也可扮演为Eden，Survivor空间，每次回收的最小单元是一个region，而对于不同的region区域采取不同的策略，对于垃圾回收产生了更为优秀的结果。G1收集器会自动的去评估每个region区域的回收价值大小，根据<code>-XX:MaxGCPauseMillis</code>参数来确定回收策略，我们叫这种模式为<code>Mixed GC</code>。相比于之前，有两个优点，一个是变得更加灵活了，有回收策略了；另一个是不再需要连续了，内存空间的分配也会更加合理。<br>那么在我们了解了这种化整为零的内存空间分配策略后，我们需要知道整个G1回收器的垃圾回收流程：<br><img src="/images/G1.png"></p>
<h3 id="5-3-2-Young-GC"><a href="#5-3-2-Young-GC" class="headerlink" title="5.3.2 Young GC"></a>5.3.2 Young GC</h3><p>程序在运行的时候，随着对象不断创建，许多内存进行Eden区，当Eden区被占满时，会自动触发一次<code>Young GC</code>，此时会引起STW，此时G1涉及的对象只有Eden区和Survivor区,回收的内存进入<code>Cset</code>中进行回收;运用了复制算法,将存活单位复制到新的survivor区域.</p>
<ul>
<li>扫描根和rset, 此时会有一个小的短暂的STW;</li>
<li>运用写屏障更新rset,此时所有跨代引用对象不会被视为垃圾;</li>
<li>复制算法,实现Eden区和Survivor区的存活对象到新的Survivor区的转移</li>
<li>处理软弱虚引用.</li>
</ul>
<h3 id="5-3-3-Young-GC-Concurrent-Marking"><a href="#5-3-3-Young-GC-Concurrent-Marking" class="headerlink" title="5.3.3 Young GC + Concurrent Marking"></a>5.3.3 Young GC + Concurrent Marking</h3><ul>
<li>首先进行一次young GC, 同时进行一次进行一次初始标记, 标记GC Root对象, 此时会发生STW;</li>
<li>并发标记, 沿着GC Root遍历对象, 同时将所有跨代引用的标记变脏,此时属于并发标记, 不会STW; 同时也会进行SATB, 对漏标的对象进行了处理;</li>
<li>再次标记, 处理原来SATB的对象; </li>
<li>独占清理, 对每一块内存区域进行排序, 为下阶段Mixed GC做准备;</li>
<li>清理本阶段内存.</li>
</ul>
<h3 id="5-3-4-Mixed-GC"><a href="#5-3-4-Mixed-GC" class="headerlink" title="5.3.4 Mixed GC"></a>5.3.4 Mixed GC</h3><p>此时会根据<code>-XX:MaxGCPauseMillis</code>实现清理策略, 在这个阶段会做三件事:</p>
<ul>
<li>新生代的垃圾回收: Eden区存活对象到survivor区; Survivor区存活对象到新的survivor区; </li>
<li>晋升: Survivor区达到晋升阈值的对象晋升到老年代</li>
<li>老年代的垃圾回收: 结合<code>-XX:MaxGCPauseMillis</code>和上一阶段的排序, 将部分老年代进行回收;<br>这个阶段也说明了Garbage First这个垃圾回收器名字的由来, 就是会在这个环节优先回收占用内存较多的区域.<blockquote>
<p><code>-XX:MaxGCPauseMillis</code>: 最大暂停时间过短, 回收速度小于产生速度, 触发单线程的Full GC; 正常情况, 回收速度大于产生速度, Concurrent mark;<br><code>-XX：G1MixedGCLiveThresholdPercent</code>: 回收阈值,默认为65%; 达到之后才会触发回收, 过小意味着存活对象多,复制时间浪费多, STW时间长．</p>
</blockquote>
</li>
</ul>
<h2 id="5-4-新增功能"><a href="#5-4-新增功能" class="headerlink" title="5.4 新增功能"></a>5.4 新增功能</h2><h3 id="5-4-1-字符串去重"><a href="#5-4-1-字符串去重" class="headerlink" title="5.4.1 字符串去重"></a>5.4.1 字符串去重</h3><ul>
<li>优点：节省大量内存</li>
<li>缺点：略微多占用了 cpu 时间，新生代回收时间略微增加<br><code>-XX:+UseStringDeduplication</code><br>将所有新分配的字符串放入一个队列; 当新生代回收时，G1并发检查是否有字符串重复; 如果它们值一样，让它们引用同一个 char[]<blockquote>
<p>注意: 与 String.intern() 不一样; String.intern() 关注的是字符串对象; 而字符串去重关注的是 char[]; 在 JVM 内部，使用了不同的字符串表</p>
</blockquote>
</li>
</ul>
<h3 id="5-4-2-类卸载"><a href="#5-4-2-类卸载" class="headerlink" title="5.4.2 类卸载"></a>5.4.2 类卸载</h3><p>所有对象都经过并发标记后，就能知道哪些类不再被使用，当一个类加载器的所有类都不再使用，则卸载它所加载的所有类<br>-XX:+ClassUnloadingWithConcurrentMark 默认启用</p>
<h3 id="5-4-3-巨型对象管理"><a href="#5-4-3-巨型对象管理" class="headerlink" title="5.4.3 巨型对象管理"></a>5.4.3 巨型对象管理</h3><p>一个对象大于 region 的一半时，称之为巨型对象<br>G1 不会对巨型对象进行拷贝<br>回收时被优先考虑<br>G1 会跟踪老年代所有 incoming 引用，这样老年代 incoming 引用为0 的巨型对象就可以在新生代垃圾回收时处理掉</p>
<h1 id="6-ZGC收集器"><a href="#6-ZGC收集器" class="headerlink" title="6. ZGC收集器"></a>6. ZGC收集器</h1><p>是一款低延迟垃圾收集器, 希望在对吞吐量影响不大的前提下, 把垃圾收集的停顿时间缩短在10ms以内。<br>其内存布局也是和G1一样划分为若干Region区，但其具有动态的容量大小和动态创建和销毁的特征，可以分为如下三种容量：</p>
<ul>
<li>大： 不固定可以动态变换，但是一定为2MB的整数倍，不会被重分配。</li>
<li>中： 容量固定为32MB， 用来存储256KB ~ 4MB的对象</li>
<li>小： 容量固定为2MB， 用来存储0 ~ 256KB的对象</li>
</ul>
<h2 id="6-1-ZGC的垃圾收集流程"><a href="#6-1-ZGC的垃圾收集流程" class="headerlink" title="6.1 ZGC的垃圾收集流程"></a>6.1 ZGC的垃圾收集流程</h2><h3 id="6-1-1-并发标记-Concurrent-Marking"><a href="#6-1-1-并发标记-Concurrent-Marking" class="headerlink" title="6.1.1 并发标记(Concurrent Marking)"></a>6.1.1 并发标记(Concurrent Marking)</h3><p>只会在开始标记GC Root的时候有小小的停顿，剩下的标记活动都是并发运行的。和G1需要STW进行三色算法不同，ZGC的标记位置是在指针上的，所以可以省略去遍历对象图的流程</p>
<h3 id="6-1-2-并发预备重分配-Concurrent-Perpare-for-Relocation"><a href="#6-1-2-并发预备重分配-Concurrent-Perpare-for-Relocation" class="headerlink" title="6.1.2 并发预备重分配(Concurrent Perpare for Relocation)"></a>6.1.2 并发预备重分配(Concurrent Perpare for Relocation)</h3><p>通过并发标记得出的结论查询到底有哪些垃圾是需要回收的，然后将需要回收的Region区组成重分配集(Relocation Set)。因为ZGC不区分新生代老年代的特点，所以该阶段的扫描是针对全堆的，但同时相较于G1也省去了记忆集的维护成本。</p>
<blockquote>
<p>ZGC的重分配集只是决定了里面的存活对象会被重新复制到其他的Region中，而不是说回收的行为针对这个集合里面的region进行。</p>
</blockquote>
<h3 id="6-1-3-并发重分配-Concurrent-Relocation"><a href="#6-1-3-并发重分配-Concurrent-Relocation" class="headerlink" title="6.1.3 并发重分配(Concurrent Relocation)"></a>6.1.3 并发重分配(Concurrent Relocation)</h3><p>是ZGC垃圾回收的核心过程。他将重分配集中的存活对象转移到新的Region中，并在重分配集中建立一个转发表(Forward Table)，用来标记重分配区中旧的Region对象到新的Region对象的转发关系。当用户访问了重分配集中的对象，那么就会被预置的内存屏障所截获，根据转发表的记录转发到新的对象上，并将引用更改到新的对象上，使其指向新的对象。这种行为被称为指针的“自愈”行为。<br>一旦region的存活对象都被复制完毕，那么这个region就可以进行下一轮的内存分配。但是转发表却得等到所有重分配集中的region对象复制完毕才可删除。</p>
<h3 id="6-1-4-并发重映射-Concurrent-Remap"><a href="#6-1-4-并发重映射-Concurrent-Remap" class="headerlink" title="6.1.4 并发重映射(Concurrent Remap)"></a>6.1.4 并发重映射(Concurrent Remap)</h3><p>重映射指的是修正整个堆中指向重分配集中的旧对象的所有引用。，可以合并到下一次的并发标记的流程进行，节约一次遍历对象图的操作。</p>
<h2 id="6-2-ZGC解决并发算法问题的关键——染色指针"><a href="#6-2-ZGC解决并发算法问题的关键——染色指针" class="headerlink" title="6.2 ZGC解决并发算法问题的关键——染色指针"></a>6.2 ZGC解决并发算法问题的关键——染色指针</h2><p>将少量标识信息存储在指针上，实现可达性分析，相较于G1的遍历对象图节约了许多时间的成本。<br>三个优点：</p>
<ol>
<li>染色指针可以使得一旦某个Region的存活对象被移走之后，这个Region立即就能被释放和重用。指针的自愈行为使得它拥有即时性，而复制算法则是需要STW进行1：1的复制后才能够实现重用。</li>
<li>染色指针可以大幅度减少在垃圾收集过程中内存屏障的使用数量，内存屏障的功能之一就是记录对象引用的变动情况，而染色指针可以接替这一工作。</li>
<li>可以作为一种可拓展的存储结构用来记录，重定位过程的相关数据，日后可以进一步提升性能。日后标志位可用的提升可以使得其更有扩展性。</li>
</ol>

    </div>

    
    
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Daorong Xing
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://daorongxing.gitee.io/post/370/" title="GC">https://daorongxing.gitee.io/post/370/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">
          
          <div class="post-tags">
              <a href="/tags/JVM/" rel="tag"><i class="fa fa-tag"></i> JVM</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/post/18463/" rel="prev" title="C++指针和结构体的浅显认知">
      <i class="fa fa-chevron-left"></i> C++指针和结构体的浅显认知
    </a></div>
      <div class="post-nav-item">
    <a href="/post/3701/" rel="next" title="Spring_1">
      Spring_1 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1-%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E5%AF%B9%E8%B1%A1%E5%8F%AF%E4%BB%A5%E8%A2%AB%E5%9B%9E%E6%94%B6"><span class="nav-text">1. 如何判断对象可以被回收</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-1-%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%B3%95"><span class="nav-text">1.1 引用计数法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-2-%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90%E7%AE%97%E6%B3%95"><span class="nav-text">1.2 可达性分析算法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-3-%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8"><span class="nav-text">1.3 四种引用</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-1-%E5%BC%BA%E5%BC%95%E7%94%A8"><span class="nav-text">1.3.1 强引用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-2-%E8%BD%AF%E5%BC%95%E7%94%A8"><span class="nav-text">1.3.2 软引用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-3-%E5%BC%95%E7%94%A8%E9%98%9F%E5%88%97"><span class="nav-text">1.3.3 引用队列</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-4-%E5%BC%B1%E5%BC%95%E7%94%A8"><span class="nav-text">1.3.4 弱引用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-5-%E8%99%9A%E5%BC%95%E7%94%A8"><span class="nav-text">1.3.5 虚引用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-6-%E7%BB%88%E7%BB%93%E5%99%A8%E5%BC%95%E7%94%A8"><span class="nav-text">1.3.6 终结器引用</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2-%E4%B8%89%E7%A7%8D%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E7%AE%97%E6%B3%95"><span class="nav-text">2. 三种垃圾收集算法</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#2-1-%E6%A0%87%E8%AE%B0%E2%80%94%E2%80%94%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95"><span class="nav-text">2.1 标记——清除算法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-2-%E6%A0%87%E8%AE%B0%E2%80%94%E2%80%94%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95"><span class="nav-text">2.2 标记——复制算法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-3-%E6%A0%87%E8%AE%B0%E2%80%94%E2%80%94%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95"><span class="nav-text">2.3 标记——整理算法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E5%9E%83%E5%9C%BE%E5%88%86%E4%BB%A3%E5%9B%9E%E6%94%B6"><span class="nav-text">3. 垃圾分代回收</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-1-%E5%88%86%E4%BB%A3%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6"><span class="nav-text">3.1 分代垃圾回收机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-2-VM%E7%9B%B8%E5%85%B3%E5%8F%82%E6%95%B0"><span class="nav-text">3.2 VM相关参数</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#4-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="nav-text">4. 垃圾回收器</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#4-1-%E4%B8%B2%E8%A1%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8-Serial-x2F-Serial-Old"><span class="nav-text">4.1 串行垃圾回收器(Serial&#x2F; Serial Old)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-2-%E5%90%9E%E5%90%90%E9%87%8F%E4%BC%98%E5%85%88-Parallel-x2F-Parallel-Old"><span class="nav-text">4.2 吞吐量优先(Parallel&#x2F; Parallel Old)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-2-%E5%93%8D%E5%BA%94%E6%97%B6%E9%97%B4%E4%BC%98%E5%85%88-CMS-Concurrent-Marking-Sweep"><span class="nav-text">4.2 响应时间优先(CMS Concurrent Marking Sweep)</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#5-G1"><span class="nav-text">5. G1</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#5-1-%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5%E7%9A%84%E4%BA%86%E8%A7%A3"><span class="nav-text">5.1 基础概念的了解</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-1-%E5%86%99%E5%B1%8F%E9%9A%9C"><span class="nav-text">5.1.1 写屏障</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-2-%E8%AE%B0%E5%BF%86%E9%9B%86%E5%92%8C%E5%8D%A1%E8%A1%A8"><span class="nav-text">5.1.2 记忆集和卡表</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-2-%E4%B8%89%E8%89%B2%E6%A0%87%E8%AE%B0%E7%AE%97%E6%B3%95"><span class="nav-text">5.2 三色标记算法</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-1-%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5"><span class="nav-text">5.2.1 基本概念</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-2-%E9%81%8D%E5%8E%86%E8%BF%87%E7%A8%8B"><span class="nav-text">5.2.2 遍历过程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-3-%E5%AD%98%E5%9C%A8%E9%97%AE%E9%A2%98"><span class="nav-text">5.2.3 存在问题</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-4-%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88"><span class="nav-text">5.2.4 解决方案</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-3-G1%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8%E7%9A%84%E5%9E%83%E5%9C%BE%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B"><span class="nav-text">5.3 G1垃圾回收器的垃圾处理流程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-1-%E4%BB%A5rigion%E4%B8%BA%E5%88%86%E5%8C%BA%E7%9A%84%E5%9B%9E%E6%94%B6%E8%8C%83%E5%9B%B4%E6%94%B9%E5%8F%98"><span class="nav-text">5.3.1 以rigion为分区的回收范围改变</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-2-Young-GC"><span class="nav-text">5.3.2 Young GC</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-3-Young-GC-Concurrent-Marking"><span class="nav-text">5.3.3 Young GC + Concurrent Marking</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-4-Mixed-GC"><span class="nav-text">5.3.4 Mixed GC</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-4-%E6%96%B0%E5%A2%9E%E5%8A%9F%E8%83%BD"><span class="nav-text">5.4 新增功能</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-4-1-%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8E%BB%E9%87%8D"><span class="nav-text">5.4.1 字符串去重</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-4-2-%E7%B1%BB%E5%8D%B8%E8%BD%BD"><span class="nav-text">5.4.2 类卸载</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-4-3-%E5%B7%A8%E5%9E%8B%E5%AF%B9%E8%B1%A1%E7%AE%A1%E7%90%86"><span class="nav-text">5.4.3 巨型对象管理</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#6-ZGC%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-text">6. ZGC收集器</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#6-1-ZGC%E7%9A%84%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E6%B5%81%E7%A8%8B"><span class="nav-text">6.1 ZGC的垃圾收集流程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#6-1-1-%E5%B9%B6%E5%8F%91%E6%A0%87%E8%AE%B0-Concurrent-Marking"><span class="nav-text">6.1.1 并发标记(Concurrent Marking)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#6-1-2-%E5%B9%B6%E5%8F%91%E9%A2%84%E5%A4%87%E9%87%8D%E5%88%86%E9%85%8D-Concurrent-Perpare-for-Relocation"><span class="nav-text">6.1.2 并发预备重分配(Concurrent Perpare for Relocation)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#6-1-3-%E5%B9%B6%E5%8F%91%E9%87%8D%E5%88%86%E9%85%8D-Concurrent-Relocation"><span class="nav-text">6.1.3 并发重分配(Concurrent Relocation)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#6-1-4-%E5%B9%B6%E5%8F%91%E9%87%8D%E6%98%A0%E5%B0%84-Concurrent-Remap"><span class="nav-text">6.1.4 并发重映射(Concurrent Remap)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-2-ZGC%E8%A7%A3%E5%86%B3%E5%B9%B6%E5%8F%91%E7%AE%97%E6%B3%95%E9%97%AE%E9%A2%98%E7%9A%84%E5%85%B3%E9%94%AE%E2%80%94%E2%80%94%E6%9F%93%E8%89%B2%E6%8C%87%E9%92%88"><span class="nav-text">6.2 ZGC解决并发算法问题的关键——染色指针</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Daorong Xing"
      src="/images/avatar.gif">
  <p class="site-author-name" itemprop="name">Daorong Xing</p>
  <div class="site-description" itemprop="description">这是我的个人博客，记录计算机学习之路！</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">10</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">4</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/yourname" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;yourname" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i></a>
      </span>
      <span class="links-of-author-item">
        <a href="/2630168952@qq.com" title="E-Mail → 2630168952@qq.com"><i class="fa fa-envelope fa-fw"></i></a>
      </span>
      <span class="links-of-author-item">
        <a href="/2630168952@qq.com" title="QQ → 2630168952@qq.com"><i class="fa fa-qq fa-fw"></i></a>
      </span>
  </div>



<script type="text/javascript" charset="utf-8" src="/js/tagcloud.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/tagcanvas.js"></script>
<div class="widget-wrap">
    <h3 class="widget-title">标签云</h3>
    <div id="myCanvasContainer" class="widget tagcloud">
        <canvas width="250" height="250" id="resCanvas" style="width=100%">
            <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/JVM/" rel="tag">JVM</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Spring/" rel="tag">Spring</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%8C%87%E9%92%88/" rel="tag">指针</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%AE%97%E6%B3%95/" rel="tag">算法</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BB%93%E6%9E%84%E4%BD%93/" rel="tag">结构体</a><span class="tag-list-count">1</span></li></ul>
        </canvas>
    </div>
</div>


      </div>
        <div class="back-to-top motion-element">
          <i class="fa fa-arrow-up"></i>
          <span>0%</span>
        </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Daorong Xing</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
      <span class="post-meta-item-text">站点总字数：</span>
    <span title="站点总字数">91k</span>
</div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>
  <script src="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.js"></script>
  <script src="//cdn.jsdelivr.net/npm/pangu@4/dist/browser/pangu.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

</body>
</html>
